#pragma once

#include "transform.h"

#include <vector>

namespace kp {

	struct Intersection;

	enum class SdfRecordType {
		FLASE_,
		TRUE_,
		A_,
		B_
	};

	class Shape {
	public:
		Shape(Ref<Transform>o2w, Ref<Transform>w2o, bool ro)
			:objectToWorld(o2w), worldToObject(w2o), reverseOrientation(ro)
		{}

		virtual bool Intersect(const Ray& r, float& tHit, float& epsilon, Intersection& dg, SdfRecordType& sdfRec) const = 0;

		virtual bool Sdf(const Point3f& p, float& sdfResult) const = 0;

		virtual BBox3f GetBBox() const = 0;

		// geometry shading
		virtual void GetShadingGeometry(const Transform& o2w,
			const Intersection& dg, Intersection& dgShading) const {
		}

		// surface area
		virtual float Area() const { return .0f; }

	protected:
		Ref<Transform> objectToWorld, worldToObject;

		bool reverseOrientation;
	};

	class Sphere : public Shape {
	public:
		explicit Sphere(Ref<Transform> o2w, Ref<Transform> w2o, bool ro, float radius = 1.0f)
			:Shape(o2w, w2o, ro), radius(radius)
		{}

		virtual bool Intersect(const Ray& r, float& tHit, float& epsilon, Intersection& dg, SdfRecordType& sdfRec) const override;
		
		virtual bool Sdf(const Point3f& p, float& sdfResult) const override;
		
		virtual BBox3f GetBBox() const override;

		virtual float Area() const override { return Pi * 4.0f * radius * radius; }

	private:
		const float radius;

		constexpr static float phi_max = DegreesToRadians(360.0f);
	};

	struct TriangleMesh {
		const int nTrianlges, nVertices;
		Scope<Point3f[]> p;
		Scope<Normal[]>  n;
		Scope<Vec3f[]>	 s;
		Scope<Point2f[]> uv;
		std::vector<Face>faces;

		TriangleMesh(Ref<Transform>o2w, bool ro,
			int nt, int nv, const Face* vi, const Point3f* p, const Normal* n, const Vec3f* s, const Point2f* uv);
	};

	class Triangle : public Shape {
	public:
		explicit Triangle(Ref<Transform> o2w, Ref<Transform> w2o, bool ro, Ref<TriangleMesh>mesh, int index)
			:Shape(o2w, w2o, ro), mesh(mesh), index(index) {
		}

		virtual bool Intersect(const Ray& r, float& tHit, float& epsilon, Intersection& dg, SdfRecordType& sdfRec) const override;
		
		virtual bool Sdf(const Point3f& p, float& sdfResult) const override;
		
		virtual BBox3f GetBBox() const override;

		virtual float Area() const override;

	private:
		Ref<TriangleMesh> mesh;
		int index;

	public:
		static std::vector<Ref<Shape>> CreateTriangleMesh(
			Ref<Transform> o2w, Ref<Transform> w2o, bool ro,
			int nt, int nv,
			const Face* fa,
			const Point3f* p,
			const Normal* n,
			const Vec3f* s,
			const Point2f* uv);

	};


}